#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>

#include "lin_al.h"

static signed char
sgn(unsigned int r, unsigned int c)
{
	if ( !(r+c & 01) )
		return (signed char) 1;
	if (r+c & 01)
		return (signed char)-1;
	return 0;
}

double
det_minor(struct matrix* A, unsigned int row_st, unsigned int row_fin,
		unsigned int col_st, unsigned int col_fin)
{
	double det = 0.;
	register unsigned int k, i, j, j_t;
	struct matrix *tmp_a;

	if (row_fin - row_st == 1 && col_fin - col_st == 1) {
		det = A->data[row_st][col_st] * A->data[row_fin][col_fin] -
			A->data[row_fin][col_st] * A->data[row_st][col_fin];
		return det; /* Bottom of recursion */
	}

	tmp_a = my_malloc(sizeof *tmp_a);
	tmp_a->m = A->m - 1u;
	tmp_a->n = A->n - 1u;
	matrix_calloc(tmp_a);
	for (k = col_st ; k <= col_fin ; ++k) {
		for (i = 0 ; i <= row_fin - row_st - 1 ; ++i)
			for (j_t = j = 0 ; j <= col_fin - col_st ; ++j)
				if (j != k - col_st) {
					tmp_a->data[i][j_t] =
						A->data[i+row_st+1][j+col_st];
					++j_t;
				}

		(void) fputs("---\n", stderr);
		fprint_matrix(tmp_a, stderr);
		(void) fputs("---\n", stderr);
		det += sgn(row_st, k) *
			A->data[row_st][k] * det_minor(tmp_a, 0U,
			row_fin - 1U - row_st, 0U, col_fin - 1U - col_st);
	}
	matrix_free(tmp_a);
	return det;
}

extern double
det(struct matrix* A)
{
	return det_minor(A, 0U, A->m - 1U, 0U, A->n - 1U);
}

int
main(int argc, char **argv)
{
	struct matrix	*a;
	unsigned int	i = 0U, j = 0U;

	a = my_malloc(sizeof *a);
	fputs("Enter m and then n: ", stdout);
	fflush(stdout);
	scanf("%u%u", &(a->m), &(a->n));
	if (a->m != a->n || a->m <= 1 || a->n <= 1) {
		printf("Excuse me?\n");
		exit(EXIT_FAILURE);
	}
	matrix_calloc(a);
	printf("Enter a matrix with m=%u rows and n=%u columns\n",
			a->m, a->n);
	for (i = 0 ; i < a->m ; ++i)
		for (j = 0 ; j < a->n ; ++j)
			scanf("%lf", &(a->data[i][j]));
	printf("det(A) = %g\n", det(a));

	matrix_free(a);
	return 0;
}
